package cn.bytets.rpc.api.balance;

import cn.bytets.rpc.api.invoke.Invocation;
import cn.bytets.rpc.api.invoke.Invoker;
import cn.bytets.rpc.common.URL;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 负载均衡-最少调用
 * 这个平衡算法里面的权重作用不大
 */
public class LeastActiveLoadBalance extends AbstractLoadBalance{

    public static final String NAME = "leastactive";

    public final static AtomicInteger one = new AtomicInteger();

    public final static AtomicInteger random = new AtomicInteger();

    @Override
    protected Invoker doSelect(List<Invoker> list, URL url, Invocation invocation) {

        int length = list.size();
        /**
         * 保存 list 权重值
         */
        int[] weights = new int[length];
        /**
         * 所有调用程序中活动最少的值
         */
        int leastActive = -1;
        /**
         * 命中的最少调用Invoker
         */
        int leastCount = 0;
        /**
         * 保存命中的Invoker索引
         */
        int[] leasWeight = new int[length];

        int firstWeight = 0;
        /**
         * 标准权重是否一样
         */
        boolean sameWeight = true;
        /**
         * 命中的权重累加,后续需要使用这个权重值进行随机
         */
        int totalWeight = 0;

        for (int i = 0; i < length; i++) {

            Invoker invoker = list.get(i);

            int active = RpcStatus.getStatus(invoker.getUrl(),invocation.getMethodName()).getActive();

            int weight = getWeight(invoker,invocation);

            weights[i] = weight;
            /**
             * 表示第一次遍历 或者 有比上一次活跃更小的服务
             */
            if (leastActive==-1||active<leastActive){
                leastActive = active;
                leastCount = 1;
                leasWeight[0] = i;
                totalWeight = weight;
                firstWeight = weight;
                sameWeight = true;
                /**
                 * 活跃次数一样的情况,服务器第一次启动的时候
                 */
            } else if (active == leastActive){
                /**
                 * 保存索引
                 */
                leasWeight[leastCount++] = i;
                totalWeight += weight;
                /**
                 * 权重不一样
                 */
                if (sameWeight&&i>0&&firstWeight!=weight){
                    sameWeight = false;
                }
            }

        }
        /**
         * 如果我们只有一个具有最小活动值的调用程序，则直接返回此调用程序。
         */
        if (leastCount == 1) {
            one.incrementAndGet();
            return addActive(list.get(leasWeight[0]),invocation);
        }
        /**
         * 这里的totalWeight会有小于0的情况?
         */
        if (!sameWeight&&totalWeight>0){
            random.incrementAndGet();
            /**
             * 将命中的权重总和进行随机
             */
            int offset = ThreadLocalRandom.current().nextInt(totalWeight);
            for (int i = 0; i < leastCount; i++) {
                /**
                 * leastIndex 是最少调用在所有的weights数组中的索引
                 */
                int leastIndex = leasWeight[i];
                offset -= weights[leastIndex];
                if (offset<0){
                    return addActive(list.get(leastIndex),invocation);
                }
            }
        }
        return addActive(list.get(leasWeight[ThreadLocalRandom.current().nextInt(leastCount)]),invocation);
    }

    public Invoker addActive(Invoker invoker,Invocation invocation){
        RpcStatus.getStatus(invoker.getUrl(),invocation.getMethodName()).addActive();
        return invoker;
    }
}
